/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.persistence.hibernate.core;

import com.blandware.atleap.common.util.PartialCollection;
import com.blandware.atleap.common.util.QueryInfo;
import com.blandware.atleap.model.core.*;
import com.blandware.atleap.persistence.core.ContentPageDAO;
import com.blandware.atleap.persistence.exception.DeleteException;
import org.hibernate.Hibernate;
import org.hibernate.type.Type;

import java.util.*;

/**
 * <p>Hibernate implementation of ContentPageDAO</p>
 * <p><a href="ContentPageDAOHibernate.java.html"><i>View Source</i></a></p>
 *
 * @author Andrey Grebnev <a href="mailto:andrey.grebnev@blandware.com">&lt;andrey.grebnev@blandware.com&gt;</a>
 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.44 $ $Date: 2008/06/23 13:24:53 $
 */
public class ContentPageDAOHibernate extends PageDAOHibernate implements ContentPageDAO {

	/**
	 * Creates new instance of NewsDAOHibernate
	 */
	public ContentPageDAOHibernate() {
	}

	/**
	 * @see com.blandware.atleap.persistence.core.ContentPageDAO#createContentPage(com.blandware.atleap.model.core.ContentPage, com.blandware.atleap.model.core.Layout)
	 */
	public Long createContentPage(ContentPage contentPage, Layout layout) {
        contentPage.setLastUpdatedDatetime(new Date());
        contentPage.setUsageCounter(new Integer(0));
		layout.addContentPage(contentPage);
		Long contentPageId = (Long) getHibernateTemplate().save(contentPage);

		// title
		ContentField fTitle = new ContentField();
		fTitle.setIdentifier("title");
		fTitle.setType(ContentField.LINE_TYPE);
		fTitle.setInternal(Boolean.TRUE);
		contentPage.addContentField(fTitle);
		getHibernateTemplate().save(fTitle);

		// description
		ContentField fDescription = new ContentField();
		fDescription.setIdentifier("description");
		fDescription.setType(ContentField.LINE_TYPE);
		fDescription.setInternal(Boolean.TRUE);
		contentPage.addContentField(fDescription);
		getHibernateTemplate().save(fDescription);

		// keywords
		ContentField fKeywords = new ContentField();
		fKeywords.setIdentifier("keywords");
		fKeywords.setType(ContentField.LINE_TYPE);
		fKeywords.setInternal(Boolean.TRUE);
		contentPage.addContentField(fKeywords);
		getHibernateTemplate().save(fKeywords);

        // Fill in content field values for title, keywords and description
        // fields for every content locale
		List contentLocales = executeFind("from ContentLocale l", true, "query.listContentLocales");
		for ( int i = 0; i < contentLocales.size(); i++ ) {
			ContentLocale contentLocale = (ContentLocale) contentLocales.get(i);
			String localeIdentifier = contentLocale.getIdentifier();

            // values for title, description and keywords in that locale
			String valueTitle = (String) contentPage.getTitle().get(localeIdentifier);
			String valueDescription = (String) contentPage.getDescription().get(localeIdentifier);
			String valueKeywords = (String) contentPage.getKeywords().get(localeIdentifier);

			//title
			ContentFieldValue fvTitle = new ContentFieldValue();
			fvTitle.setContentLocale(contentLocale);
			if ( valueTitle != null ) {
				fvTitle.setSimpleValue(valueTitle);
			} else {
				fvTitle.setSimpleValue("");
			}
			fvTitle.setLastUpdatedDatetime(new Date());
			fTitle.addContentFieldValue(fvTitle);
			getHibernateTemplate().save(fvTitle);

			//description
			ContentFieldValue fvDescription = new ContentFieldValue();
			fvDescription.setContentLocale(contentLocale);
			if ( valueDescription != null ) {
				fvDescription.setSimpleValue(valueDescription);
			} else {
				fvDescription.setSimpleValue("");
			}
			fvDescription.setLastUpdatedDatetime(new Date());
			fDescription.addContentFieldValue(fvDescription);
			getHibernateTemplate().save(fvDescription);

			//keywords
			ContentFieldValue fvKeywords = new ContentFieldValue();
			fvKeywords.setContentLocale(contentLocale);
			if ( valueKeywords != null ) {
				fvKeywords.setSimpleValue(valueKeywords);
			} else {
				fvKeywords.setSimpleValue("");
			}
			fvKeywords.setLastUpdatedDatetime(new Date());
			fKeywords.addContentFieldValue(fvKeywords);
			getHibernateTemplate().save(fvKeywords);
		}

		return contentPageId;
	}

	/**
	 * @see com.blandware.atleap.persistence.core.ContentPageDAO#retrieveContentPage(java.lang.Long)
	 */
	public ContentPage retrieveContentPage(Long contentPageId) {
		String hql = new StringBuffer("select distinct page, title.contentLocale.identifier, title.simpleValue, description.simpleValue, keywords.simpleValue ")
		        .append("from ContentPage as page left outer join ")
		        .append("page.contentFields titleField left outer join titleField.contentFieldValues as title left outer join ")
		        .append("page.contentFields descriptionField left outer join descriptionField.contentFieldValues as description ")
		        .append("left outer join page.contentFields keywordsField left outer join keywordsField.contentFieldValues as keywords ")
		        .append("where ")
		        .append("title.contentLocale = description.contentLocale and title.contentLocale = keywords.contentLocale ")
		        .append("and titleField.identifier = 'title' ")
		        .append("and descriptionField.identifier = 'description' and keywordsField.identifier = 'keywords' and page.id = ?")
		        .toString();

        // Get a list of quintuples (page, locale identifier, title value,
        // description value, keywords value)
		List list = executeFind(hql, new Object[]{contentPageId});
        // Assign those values to corresponding pages -- get list of pages with
        // title, description and keywords correctly assigned
		List result = setLocalizableFields(list);
		if ( result == null || result.size() <= 0 ) {
			return null;
		} else {
			return (ContentPage) result.get(0);
		}
	}

	/**
	 * @see com.blandware.atleap.persistence.core.ContentPageDAO#updateContentPage(com.blandware.atleap.model.core.ContentPage, com.blandware.atleap.model.core.Layout)
	 */
	public void updateContentPage(ContentPage contentPage, Layout layout) {
		ContentPage old = layout.updateContentPage(contentPage);
        removeFromCache(old);
		getHibernateTemplate().update(contentPage);

        // Update internal fields (title, description and keywords) of this page
		for ( int i = 0; i < contentPage.getContentFields().size(); i++ ) {
			ContentField contentField = (ContentField) contentPage.getContentFields().get(i);
			Map values;
			if ( "title".equalsIgnoreCase(contentField.getIdentifier()) ) {
				values = contentPage.getTitle();
			} else if ( "description".equalsIgnoreCase(contentField.getIdentifier()) ) {
				values = contentPage.getDescription();
			} else if ( "keywords".equalsIgnoreCase(contentField.getIdentifier()) ) {
				values = contentPage.getKeywords();
			} else {
                continue;
            }
			for ( int j = 0; j < contentField.getContentFieldValues().size(); j++ ) {
				ContentFieldValue contentFieldValue = (ContentFieldValue) contentField.getContentFieldValues().get(j);
				String localeIdentifier = contentFieldValue.getContentLocale().getIdentifier();
				String value = (String) values.get(localeIdentifier);
				if ( value != null ) {
					contentFieldValue.setSimpleValue(value);
				} else {
					contentFieldValue.setSimpleValue("");
				}
				contentFieldValue.setLastUpdatedDatetime(new Date());
				contentFieldValue.getContentField().updateContentFieldValue(contentFieldValue);
				getHibernateTemplate().update(contentFieldValue);
			}
		}
	}

	/**
	 * @see com.blandware.atleap.persistence.core.ContentPageDAO#deleteContentPage(com.blandware.atleap.model.core.ContentPage)
	 */
	public void deleteContentPage(ContentPage contentPage) throws DeleteException {
        removeAssociations(contentPage);

		getHibernateTemplate().delete(contentPage);
		contentPage.getLayout().removeContentPage(contentPage);

		// break relations between roles and deleted content page
		List roles = new ArrayList(contentPage.getRoles());
		for ( int i = 0; i < roles.size(); i++ ) {
			Role role = (Role) roles.get(i);
			contentPage.removeRole(role);
		}
	}

	// ~ Additional methods ================================================================

	/**
	 * @see com.blandware.atleap.persistence.core.ContentPageDAO#listContentPages(com.blandware.atleap.common.util.QueryInfo)
	 */
	public PartialCollection listContentPages(QueryInfo queryInfo) {
		String whereClause = "";
		String orderByClause = "";
		if ( queryInfo != null ) {
			whereClause = queryInfo.getWhereClause();
			orderByClause = queryInfo.getOrderByClause();
			if ( whereClause == null || whereClause.length() == 0 ) {
				whereClause = "";
			}
			if ( orderByClause != null && orderByClause.length() != 0 ) {
				orderByClause = " order by " + orderByClause;
			} else {
				orderByClause = "";
			}
		}

		List list;
		Integer total = null;

		String localeIdentifier = null;
		if ( queryInfo != null ) {
			if ( queryInfo.getQueryParameters() != null ) {
				localeIdentifier = (String) queryInfo.getQueryParameters().get("localeIdentifier");
			}
		}

		boolean localeIdentifierPresent = localeIdentifier != null && localeIdentifier.length() > 0;

		String hqlPart;
		ArrayList args = new ArrayList();
		if ( localeIdentifierPresent ) {
			args.add(localeIdentifier);
			hqlPart = new StringBuffer("from ContentPage as page ")
			        .append("left outer join page.contentFields as titleField ")
			        .append("left outer join page.contentFields as descriptionField ")
			        .append("left outer join page.contentFields as keywordsField ")
			        .append("left outer join titleField.contentFieldValues as title ")
			        .append("left outer join descriptionField.contentFieldValues as description ")
			        .append("left outer join keywordsField.contentFieldValues as keywords ")
			        .append("left outer join page.roles as role ")
			        .append("where ")
			        .append("titleField.identifier = 'title' and descriptionField.identifier = 'description' and keywordsField.identifier = 'keywords' ")
			        .append("and title.contentLocale = description.contentLocale and title.contentLocale = keywords.contentLocale ")
			        .append("and title.contentLocale.identifier = ? ")
			        .toString();
			if ( whereClause.length() > 0 ) {
				hqlPart += "and " + whereClause;
			}
		} else {
			hqlPart = "from ContentPage page left outer join page.roles as role ";
			if ( whereClause.length() > 0 ) {
				hqlPart += "where " + whereClause;
			}
		}

		if ( queryInfo != null && (queryInfo.getLimit() != null || queryInfo.getOffset() != null) ) {
			// query count
			String hqlForTotal = "select count(distinct page.id) " + hqlPart;
			total = findUniqueIntegerResult(hqlForTotal, args.toArray());
			if ( total == null ) {
				total = new Integer(0);
			}
		}

        // If we don't have any info about the total number of results yet or
        // we know that there's something that will be found, then fetch data
		if ( total == null || total.intValue() > 0 ) {
			String hql;
			if ( localeIdentifierPresent ) {
				hql = "select distinct page, title.simpleValue, description.simpleValue, keywords.simpleValue " + hqlPart + orderByClause;
			} else {
				hql = "select distinct page " + hqlPart + orderByClause;
			}
			list = executeFind(hql, queryInfo, args.toArray());
			if ( total == null ) {
				total = new Integer(list.size());
			}
			if ( localeIdentifierPresent ) {
                // Replace each tuple in list with a page with title, description
                // and keywords assigned
				for ( ListIterator i = list.listIterator(); i.hasNext(); ) {
					Object[] objects = (Object[]) i.next();
					ContentPage contentPage = (ContentPage) objects[0];
					contentPage.getTitle().put(localeIdentifier, objects[1]);
					contentPage.getDescription().put(localeIdentifier, objects[2]);
					contentPage.getKeywords().put(localeIdentifier, objects[3]);
					i.set(contentPage);
				}
			}
		} else {
			list = new ArrayList();
		}

		return new PartialCollection(list, total);

	}

    // ~ Finders ================================================================

	/**
	 * @see com.blandware.atleap.persistence.core.ContentPageDAO#findContentPageByUri(java.lang.String)
	 */
	public ContentPage findContentPageByUri(String contentPageUri) {
		return (ContentPage) findUniqueResult("from ContentPage page where page.uri = ?", new Object[]{contentPageUri}, true, "query.findContentPageByUri");
	}

    /**
     * @see com.blandware.atleap.persistence.core.ContentPageDAO#findUnpublishedContentPages(java.util.Date)
     */
    public List findUnpublishedContentPages(Date date) {
		String hql = "from ContentPage page where page.active = 'F' and page.publicationDate <= ? and page.expirationDate > ?";
		return executeFind(hql, new Object[]{date, date}, new Type[]{Hibernate.DATE, Hibernate.DATE});
    }

    /**
     * @see com.blandware.atleap.persistence.core.ContentPageDAO#findExpiredContentPages(java.util.Date)  
     */
    public List findExpiredContentPages(Date date) {
		String hql = "from ContentPage page where page.active = 'T' and page.expirationDate <= ?";
		return executeFind(hql, new Object[]{date}, new Type[]{Hibernate.DATE});
    }

    // ~ Helper methods

}
